home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 426-450 / disk_429 / dr / source / fastex.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  27KB  |  1,042 lines

  1. /* NEEDED:
  2. Make more robust for corrupt disk - make sure continuable (sometimes?) past
  3.     not-a-dos-disk error, in case of one bad block pointer or something.
  4. Handle environment DE_MASK correctly  (What am I supposed to do with it?)
  5. Handle odd unevenly-dividable-by-maxtransfer track size.
  6. PROFILING CONCLUSIONS:  doing Extract and Hash hardly helped, so don't bother
  7.     with any others.
  8. Test lock for valid key?
  9. Maybe have it asynchronously anticipate the next track needed with a SendIO.
  10.     In the event that the track anticipated turns out not to be the one
  11.     it wants, just chew it into the maybe tree and read another.  Is this
  12.     worthwhile if we are using full asynchronicity in Dr?  Probly not.
  13. Non-512-byte blocks someday?
  14. Attempt an ACTION_GET_BLOCK version and compare performance?  The tricky part
  15.     is that GET_BLOCK-ing an unused block throws up a read/write error
  16.     requester, and we want the other requesters to happen normally.  Fuck
  17.     it, too much trouble.
  18. */
  19.  
  20. /* ======================================================================= */
  21.  
  22. /* FastExNext is by Paul Kienitz 1989-1990, public domain. */
  23.  
  24.  
  25. #include <exec/exec.h>
  26. #include <exec/errors.h>
  27. #include <devices/trackdisk.h>
  28. #include <libraries/filehandler.h>
  29. #include <libraries/dosextens.h>
  30. #include <string.h>
  31. #include <Paul.h>
  32.  
  33.  
  34. #ifdef LEAKAGE
  35.  
  36. import adr AllocYell(long a, long b, str c, long d);
  37. import void FreeYell(adr a, long b, str c, long d);
  38. #define AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
  39. #define FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
  40. #define _AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
  41. #define _FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
  42.  
  43. #endif
  44.  
  45.  
  46. /* no intuition/intuition.h, because all we need out of it are these three: */
  47.  
  48. #define JAM2 1L
  49. #define DISKINSERTED 0x00008000L
  50.  
  51. struct IntuiText {
  52.     ubyte FrontPen, BackPen;
  53.     ubyte DrawMode;
  54.     short LeftEdge, TopEdge;
  55.     adr   ITextFont;
  56.     str   IText;        /* no ubytes here for me, thanks anyway =RJ= */
  57.     struct IntuiText *NextText;
  58. };
  59.  
  60.  
  61.  
  62. #ifdef DEBUG_IO
  63. #undef put
  64. import void put(str s), putfmt(str f, ...);        /* from pureio.c */
  65. #endif
  66.  
  67. /* =============================== Here are some types and constants: */
  68.  
  69. /* in case of out-of-date include files like I had: */
  70. #ifndef DOSTRUE
  71. #define DOSTRUE -1L
  72. #define DOSFALSE 0L
  73. #endif
  74.  
  75. #ifndef DE_DOSTYPE
  76. #define DE_MAXTRANSFER 13
  77. #define DE_MASK        14
  78. #define DE_POOTPRI     15
  79. #define DE_DOSTYPE     16
  80. #endif
  81.  
  82. typedef struct IOExtTD reek;
  83.  
  84.  
  85. #define tablesize ((TD_SECTOR >> 2) - 56)
  86.     /* GOTTA HANDLE NON-512-BYTE BLOCKS SOMEDAY? ****/
  87.  
  88. typedef struct {        /* Semi-generic DOS disk block */
  89.     long Type;            /* data = 8, extension = 16, other = 2 */
  90.     long Owner;            /* header pointer for data & ext. blocks */
  91.     long Blocks;        /* in file header, total data blocks used */
  92.     long Size;            /* data blocks in this header's table */
  93.     long First;
  94.     long Checksum;
  95.     long Table[tablesize];    /* the hashtable */
  96.     long Nothing[2];
  97.     long Protection;
  98.     long Length;        /* file size in bytes */
  99.     char Snide[92];
  100.     struct DateStamp Date;
  101.     char Name[64];        /* length in Name[0] */
  102.     long Next;            /* next dir entry with same hash value */
  103.     long Parent;        /* owning directory */
  104.     long Stench;        /* next extension block */
  105.     long Type2;        /* file header/ext = -3, directory = 2, root = 1 */
  106. } dosblock;
  107. /* I COULD CARVE A BETTER FILE SYSTEM STRUCTURE OUT OF A BANANA. */
  108.  
  109.  
  110. #define fiblet struct _fiblet
  111.  
  112. fiblet {
  113.     fiblet *next;            /* list link (in tree, next=left) */
  114.     fiblet *ready;            /* fiblets ready to return (right) */
  115.     long key, nexthash, protect, length, blocks;
  116.     struct DateStamp cheap_date;
  117.     str snide;                /* null, or points to 80 bytes */
  118.     long *wanted;            /* if dir, points to table of entries */
  119.     char name[31];            /*    with count in first lword */
  120.     char type;                /* signed integer: -3 or 2 */
  121. };                    /* only 80 bytes, not 260 */
  122.  
  123.  
  124. /* This is the "global variables" type stuff: */
  125.  
  126. typedef struct {
  127.     reek *quest;        /* disk IO request (we only need one) */
  128. #define ques quest->iotd_Req
  129.     dosblock *whole_track;    /* track buffer */
  130.     long memtype;        /* track buffer's allocmem flags */
  131.     long changes, unit;        /* floppy disk change count, unit number */
  132.     long flags;            /* device driver flags from fssm */
  133.     ushort readcommand;        /* either ETD_READ or CMD_READ */
  134.     ushort sex, sexize;        /* blocks per track, bytes per block */
  135.     long tracklen;        /* sex * sexize */
  136.     long memmask;        /* forbidden memory for track buffer?? */
  137. #ifdef FREEZIT
  138.     fiblet *freeze;        /* list of unused fiblets to recycle */
  139. #endif
  140.     fiblet *maybe;        /* tree of possibly useful files/dirs */
  141.     fiblet *parents;        /* dirs with tables in use, for cleanup */
  142.     long first_sector;        /* first sector on current track */
  143.     long mid_sector;        /* middle of most recently read track */
  144.     long tish_offset;        /* needed to convert block #s to track #s */
  145.     struct Process *me;        /* our own process node */
  146.     struct DeviceList *vol;    /* volume node on dos device list */
  147.     struct MsgPort *lask;    /* original dos handler of lock */
  148.     fiblet *lastwho;        /* for seeing when Checkbies is necessary */
  149.     bool devopen;        /* the disk's xxxx.device is open */
  150.     bool ffs;            /* it's fast file system */
  151.     bool depth;            /* we are prepared for recursive descent */
  152.     short blize;        /* the amount of data in a data block */
  153. #ifndef QUEST
  154.     long lastfail;
  155. #endif
  156.     char debna[64];        /* device driver name string */
  157. } state;
  158.  
  159. #define SS register state *s
  160.  
  161.  
  162. typedef struct {        /* an extended FileInfoBlock */
  163.     struct FileInfoBlock f;
  164.     fiblet *pard;        /* ready & wanted currently being used */
  165.     state *ss;            /* state o' th' scan */
  166. } fib;
  167.  
  168. /* #define MAGICVALUE 1253761265 */
  169.  
  170.  
  171. /* special values for the ss field (others are assumed to be pointers): */
  172.  
  173. #define NEWSHALLOW 0L
  174. #define NEWDEEP    -1L
  175. #define CLEANED    -2L
  176. #define FALLTHRU   -3L
  177.  
  178.  
  179. import struct DosLibrary *DOSBase;
  180.  
  181. adr IntuitionBase;
  182.  
  183.  
  184.  
  185. /* ================================= And now, functions */
  186.  
  187. /* First the lower level stuff... */
  188.  
  189.  
  190.  
  191. private fiblet *Pop(fiblet **lis)
  192. {
  193.     register fiblet *r = *lis;
  194.     if (r) *lis = r->next;
  195.     return r;
  196. }
  197.  
  198.  
  199.  
  200. private void Push(fiblet **lis, fiblet *v)
  201. {
  202.     v->next = *lis;
  203.     *lis = v;
  204. }
  205.  
  206.  
  207.  
  208. private fiblet *ExtractLowest(register fiblet **t)
  209. {
  210.     fiblet *r;
  211.     if (!*t) return null;
  212.     while ((*t)->next) t = & (*t)->next;
  213.     r = *t;
  214.     *t = r->ready;
  215.     return r;
  216. }
  217.  
  218.  
  219.  
  220. private adr GetFiblet(SS)
  221. {
  222.     register fiblet *r;
  223. #ifdef FREEZIT
  224.     if (!(r = Pop(&s->freeze)) && !(r = New(fiblet)))
  225. #else
  226.     if (!(r = New(fiblet)))
  227. #endif
  228. { /* DESPERATION! */
  229.     r = ExtractLowest(&s->maybe);
  230. #ifdef DEBUG_IO
  231. if (r) putfmt("**** Just forgot '%s'!\n", r->name);
  232. else put("**** AARRGH!  Completely unable to GetFiblet!\n");
  233. #endif
  234. }
  235.     if (!r) s->me->pr_Result2 = ERROR_NO_FREE_STORE;
  236.     return r;
  237. }
  238.  
  239.  
  240.  
  241. private void Freeblet(fiblet *f)
  242. {
  243.     if (f->snide) FreeMem(f->snide, (long) sizeof(fiblet));
  244.     if (f->wanted) {
  245.     FreeMem(f->wanted, (*(f->wanted) + 1L) << 2);
  246.     }
  247.     Free(fiblet, f);
  248. }
  249.  
  250.  
  251.  
  252. #ifndef AZTEC_C
  253. #define OLD_HIGH_LEVEL_VERSION
  254. #endif
  255.  
  256. #ifdef OLD_HIGH_LEVEL_VERSION
  257.  
  258. /* see, i did some profiling and concluded that these tree functions are (not
  259. surprizingly) the ones that most want optimizing.  second most wanting
  260. functions are Chew and ChewSomeMore.  ExtractLowest doesn't need it.  So the
  261. functions within this #if have been rewritten in assembly.  */
  262.  
  263. /* the idea here is that we wanna hash up the key that the tree is sorted
  264. by cuz the tree tends ta get filled with consecutive values so it gets lots
  265. of long stringy diagonals without branches all in one direction. */
  266.  
  267. private ulong Hash(register ulong k)
  268. {
  269.     ulong d = ((k & 63) << 26) | (k >> 6);
  270.     return d ^ 0x55555555;
  271. }        /* what I really want is reversed significance of bits */
  272.  
  273.  
  274.  
  275. private fiblet *Extract(register fiblet **tree, long kee)
  276. {
  277.     register fiblet *t;
  278.     long tk;
  279.     kee = Hash(kee);
  280.     while (*tree && (tk = Hash((*tree)->key)) != kee)
  281.     if (tk > kee)
  282.         tree = & (*tree)->next;
  283.     else tree = & (*tree)->ready;
  284.     if (*tree) {      /* tree points to pointer that points to right one */
  285.     t = *tree;
  286.     if (!t->next)
  287.         *tree = t->ready;
  288.     else if (!t->ready)
  289.         *tree = t->next;
  290.     else {
  291.         fiblet *tt = ExtractLowest(&t->ready);
  292.         *tree = tt;
  293.         tt->next = t->next;
  294.         tt->ready = t->ready;
  295.     }
  296.     t->next = t->ready = null;
  297.     return t;
  298.     } else return null;
  299. }
  300.  
  301. #endif
  302.  
  303.  
  304. /* and now here are the optimized versions: */
  305. #asm
  306.     public    _hAsHhAsHhAsH___HsAhHsAhHsAh_    ; too bad can't be private
  307.  
  308. _hAsHhAsHhAsH___HsAhHsAhHsAh_:
  309.     move.l    4(sp),d0        ; the arg
  310.     ror.l    #6,d0            ; RoR, gentlemen...   RoReth
  311.     eor.l    #$55555555,d0        ; alucard
  312.     rts
  313. #endasm
  314.  
  315. #define Hash hAsHhAsHhAsH___HsAhHsAhHsAh_
  316.  
  317. import ulong Hash(ulong k);
  318.  
  319.  
  320.  
  321. #asm
  322.     public    _eXtRaCtExTrAcT__TcArTxEtCaRtXe_    ; too bad not private
  323.  
  324. _eXtRaCtExTrAcT__TcArTxEtCaRtXe_:
  325.     movem.l    d5-d7/a3-a5,-(sp)    ; play it safe
  326.     move.l    28(sp),a5        ; tree
  327.     move.l    32(sp),d7        ; kee
  328.     ror.l    #6,d7            ; INLINE version of
  329.     eor.l    #$55555555,d7        ;   kee = Hash(kee)
  330.  
  331. while:    tst.l    (a5)
  332.     beq    endwh
  333.     move.l    (a5),a4
  334.     move.l    8(a4),d6        ; (*tree)->key
  335.     ror.l    #6,d6            ; Hash it inline
  336.     eor.l    #$55555555,d6
  337.     cmp.l    d6,d7
  338.     beq    endwh
  339.     move.l    (a5),a5            ; tree = & (*tree)->next
  340.     blt    while            ; if Hash((*tree)->key) < Hash(kee)
  341.     addq    #4,a5            ;   tree = & (*tree)->ready
  342.     bra    while
  343.  
  344. endwh:    move.l    (a5),a4            ; the node we'll return
  345.     move.l    a4,d0            ; pseudo tst.l
  346.     beq    ret            ; if !*tree return (null)
  347.     tst.l    (a4)            ; if !(*tree)->next
  348.     bne    rite
  349.     move.l    4(a4),(a5)        ;   *tree = (*tree)->ready
  350.     bra    finn
  351.  
  352. rite:    tst.l    4(a4)            ; else if !(*tree)->ready
  353.     bne    uh_oh
  354.     move.l    (a4),(a5)        ;   *tree = (*tree)->next
  355.     bra    finn
  356.  
  357. uh_oh:    move.l    a4,d5
  358.     addq    #4,d5            ; pseudo lea 4(a4),d5
  359.     move.l    d5,-(sp)        ; tt = ExtractLowest(&returnee->ready)
  360.     bsr    _ExtractLowest
  361.     addq    #4,sp
  362.     move.l    d0,a3
  363.     move.l    a3,(a5)            ; *tree = tt
  364.     move.l    (a4),(a3)        ; tt->next = returnee->next
  365.     move.l    4(a4),4(a3)        ; tt->ready = returnee->ready
  366.  
  367. finn:    clr.l    (a4)            ; returnee->next = null
  368.     clr.l    4(a4)            ; returnee->ready = null
  369.     move.l    a4,d0
  370. ret:    movem.l    (sp)+,d5-d7/a3-a5
  371.     rts
  372. #endasm
  373.  
  374. #define Extract eXtRaCtExTrAcT__TcArTxEtCaRtXe_
  375.  
  376. import fiblet *Extract(fiblet **tree, long kee);
  377.  
  378. /* Well, damn.  The speed gain from that was not measureable.  That routine
  379. had in the past cost significant time when it was being called too often by
  380. Checkbies, but not any more.  Oh well, as long as it's done, there's no
  381. reason to take it out...  It makes the executable a bit smaller... */
  382.  
  383.  
  384.  
  385. private void Insertt(register fiblet **tree, fiblet *f)
  386. {
  387.     long kee = Hash(f->key), tk;
  388.     while (*tree) {
  389.     tk = Hash((*tree)->key);
  390.     if (tk > kee)
  391.         tree = & (*tree)->next;
  392.     else if (tk != kee)        /* filter out dublicates */
  393.         tree = & (*tree)->ready;
  394.     else {
  395.         Freeblet(f);        /* don't bother with freeze list */
  396.         return;
  397.     }
  398.     }
  399.     *tree = f;
  400. }
  401.  
  402.  
  403.  
  404. private void FlushList(fiblet **lis)
  405. {
  406.     register fiblet *foo;
  407.     while (foo = Pop(lis))
  408.     Freeblet(foo);
  409. }
  410.  
  411.  
  412.  
  413. private void FlushTree(register fiblet **t)
  414. {
  415.     if (!*t) return;
  416.     FlushTree(&(*t)->next);
  417.     FlushTree(&(*t)->ready);
  418.     Freeblet(*t);
  419. }
  420.  
  421.  
  422.  
  423. private void WeWant(long k, register long *w)
  424. {
  425.     register short t;
  426.     for (t = 1; t <= *w; t++)
  427.     if (!w[t]) {
  428.         w[t] = k;
  429.         return;
  430.     }
  431. }
  432.  
  433.  
  434.  
  435. private bool YankIfPresent(long k, register long *w)
  436. {
  437.     register short t;
  438.     if (!k) return false;        /* occasionally needed! */
  439.     for (t = 1; t <= *w; t++)
  440.     if (w[t] == k) {
  441.         w[t] = 0;
  442.         return true;
  443.     }
  444.     return false;
  445. }
  446.  
  447.  
  448.  
  449. private long Nearest(long k, long *w)
  450. {
  451.     register short t;
  452.     register long c, d = maxint, e = 0;
  453.     for (t = 1; t <= *w; t++)
  454.     if (c = w[t]) {
  455.         c -= k;
  456.         if (c < 0) c = -c;
  457.         if (c <= d) {
  458.         d = c;
  459.         e = w[t];
  460.         }
  461.     }
  462.     return e;
  463. }
  464.  
  465.  
  466.  
  467. private void Chew(register dosblock *b, register fiblet *z, short l)
  468. /* for now assume always 512 bytes */
  469. {
  470.     register short slime = *b->Snide, maid = *b->Name;
  471.     if (slime > 79)
  472.     slime = 79;
  473.     if (maid > 30)
  474.     maid = 30;
  475.     strncpy(z->name, b->Name + 1, (size_t) maid);
  476.     z->name[maid] = '\0';
  477.     z->cheap_date = b->Date;
  478.     z->protect = b->Protection;
  479.     z->length = b->Length;
  480.     z->key = b->Owner;
  481.     z->nexthash = b->Next;
  482.     z->type = (b->Type2 > 0 ? 2 : -3);
  483.     z->blocks = (z->length + l - 1) / l;     /* ESTIMATE! */
  484.     if (slime && (z->snide = Alloc(sizeof(fiblet)))) {    /* GetFiblet?  nah. */
  485.     strncpy(z->snide, b->Snide + 1, (size_t) slime);
  486.     z->snide[slime] = '\0';
  487.     } else
  488.     z->snide = null;    /* who cares if we lose a filenote? */
  489.     z->next = z->ready = null;
  490.     z->wanted = null;
  491. }
  492.  
  493.  
  494.  
  495. private bool DirChew(dosblock *b, fiblet *z, short l)
  496. {
  497.     register short x, s = 1;
  498.     Chew(b, z, l);  /* VVV assumes 512 byte blocks ****/
  499.     if (z->type <= 0) return false;
  500.     for (x = 0; x < tablesize; x++)
  501.     if (b->Table[x]) s++;
  502.     if (z->wanted = Alloc(s << 2)) {
  503.     register long t;
  504.     *(z->wanted) = s - 1;
  505.     s = 1;
  506.     for (x = 0; x < tablesize; x++)
  507.         if (t = b->Table[x]) z->wanted[s++] = t;
  508.     return false;
  509.     }
  510. #ifdef DEBUG_IO
  511.     putfmt("**** Unable to allocate wanted table for directory '%s'!\n",
  512.         z->name);
  513. #endif
  514.     return true;
  515. }
  516.  
  517.  
  518.  
  519. private void ChewSomeMore(register fiblet *f, register fib *fibbb)
  520. {
  521.     register fib *fibb = fibbb;
  522.     memset(&fibb->f.fib_FileName, 0, 108L);
  523.     memset(&fibb->f.fib_Comment, 0, 116L);
  524.     fibb->f.fib_DiskKey = f->key;
  525.     fibb->f.fib_DirEntryType = fibb->f.fib_EntryType = f->type;
  526.     fibb->f.fib_Protection = f->protect;
  527.     fibb->f.fib_Size = f->length;
  528.     fibb->f.fib_NumBlocks = f->blocks;
  529.     fibb->f.fib_Date = f->cheap_date;
  530.     strcpy(fibb->f.fib_FileName, f->name);
  531.     if (f->snide) strcpy(fibb->f.fib_Comment, f->snide);
  532. }
  533.  
  534.  
  535.  
  536. #ifdef QUEST
  537.  
  538. #define xt struct IntuiText
  539.  
  540. private xt retry = {0, 1, JAM2, 6, 3, null, "Retry", null};
  541. private xt cancel = {0, 1, JAM2, 6, 3, null, "Cancel", null};
  542.  
  543.  
  544.  
  545. private void mixtline(register xt *t, short y, str s, xt *n)
  546. {
  547.     t->FrontPen = 0;
  548.     t->BackPen = 1;
  549.     t->DrawMode = JAM2;
  550.     t->LeftEdge = 15;
  551.     t->TopEdge = y;
  552.     t->ITextFont = null;
  553.     t->IText = s;
  554.     t->NextText = n;
  555. }
  556.  
  557.  
  558.  
  559. private void mixtmess(register xt *t, str s1, str v, str s3, SS)
  560. {
  561.     str vone = gbip((BSTR) s->vol->dl_Name);    /* who put BSTR * in there?! */
  562.     strncpy(v, vone + 1, (size_t) *vone);
  563.     v[*vone] = 0;
  564.     mixtline(t + 2, 25, s3, null);
  565.     mixtline(t + 1, 15, v, t + 2);
  566.     mixtline(t, 5, s1, t + 1);
  567. }
  568.  
  569.  
  570.  
  571. /* ================= Higher level routines */
  572.  
  573.  
  574. private bool Quest(APTR wptr, xt *tx)
  575. {
  576.     return ~(long) wptr && AutoRequest((adr) wptr, tx, &retry, &cancel,
  577.                     DISKINSERTED, 0L, 320L, 72L);
  578.     /**** is this dos 2.0 compatible??? ****/
  579. }
  580.  
  581.  
  582.  
  583. #define UFFSET 9
  584.  
  585. /* This is called by GetTrack when DoIO / WaitIO returns an error.  It
  586. returns true if the operation should be retried. */
  587.  
  588. private bool ManageError(SS)
  589. {
  590.     short air = s->ques.io_Error;
  591.     char you_nit[UFFSET + 2], volume[31];
  592.     xt lines[3];
  593.     long hair = 0;
  594.     APTR wptr = s->me->pr_WindowPtr;
  595.  
  596. /**** WHAT ABOUT TDERR_DriveInUse??  Can it happen? */
  597.     if (air == (short) IOERR_NOCMD) {
  598.     s->readcommand = CMD_READ;    /* ETD_READ unsupported (lame driver) */
  599.     return true;
  600.     }
  601. #ifdef DEBUG_IO
  602.     putfmt("**** AAGH!  Hardware error %d!\n", air);
  603. #endif
  604.     IntuitionBase = OpenLibrary("intuition.library", 0L);
  605.     if (air == (short) TDERR_NoMem) hair = ERROR_NO_FREE_STORE;
  606.     else if (air == (short) TDERR_DiskChanged) {
  607.     strcpy(you_nit, "in unit  #");
  608.     you_nit[UFFSET] = s->unit % 10 + '0';
  609.     if (s->unit >= 10)
  610.         you_nit[UFFSET - 1] = s->unit / 10 + '0';
  611.     mixtmess(lines, "Please replace volume", volume, you_nit, s);
  612.     hair = ERROR_DEVICE_NOT_MOUNTED;
  613.     while (Quest(wptr, lines))
  614.         if (s->lask == s->vol->dl_Task) {
  615.         hair = 0;
  616.         s->ques.io_Command = TD_CHANGENUM;
  617.         DoIO((struct IORequest *) s->quest);
  618.         s->changes = s->ques.io_Actual;
  619.         break;
  620.         } /* else not the right volume in the right drive */
  621.     } else {
  622.     mixtmess(lines, "Volume", volume, "has a read/write error", s);
  623.     if (!Quest(wptr, lines))
  624.         hair = ERROR_NOT_A_DOS_DISK;    /* best we can do */
  625.     }
  626.     CloseLibrary(IntuitionBase);
  627.     if (hair) {
  628.     s->me->pr_Result2 = hair;
  629.     return false;
  630.     }
  631.     return true;
  632. }
  633.  
  634.  
  635. #else  /* QUEST */
  636.  
  637.  
  638. private bool ManageError(SS)
  639. {
  640.     register long hair = 0;
  641.     register short air = s->ques.io_Error;
  642.     if (air == (short) IOERR_NOCMD) {
  643.     s->readcommand = CMD_READ;    /* lame driver don't grok ETD_READ */
  644.     return true;
  645.     } else if (s->lastfail == s->first_sector) {    /* retry each error once */
  646.     if (air == (short) TDERR_DiskChanged)
  647.         hair = ERROR_DEVICE_NOT_MOUNTED;
  648.     else if (air == (short) TDERR_NoMem)
  649.         hair = ERROR_NO_FREE_STORE;
  650.     else hair = ERROR_NOT_A_DOS_DISK;
  651.     }
  652.     s->lastfail = s->first_sector;
  653.     if (hair) {
  654.     s->me->pr_Result2 = hair;
  655.     return false;
  656.     }
  657.     return true;
  658. }
  659.  
  660. #endif QUEST
  661.  
  662.  
  663.  
  664. private void FindDrive(SS)
  665. {
  666.     struct FileSysStartupMsg *fart;
  667.     BPTR dog = ((struct RootNode *) DOSBase->dl_Root)->rn_Info;
  668.     struct DeviceNode *debb,
  669.         *devlist = bip(struct DeviceNode,
  670.                 bip(struct DosInfo, dog)->di_DevInfo);
  671.     long *feh, mat;
  672.     ubyte *deb;
  673.  
  674.     Forbid();
  675.     for (debb = devlist; debb; debb = gbip(debb->dn_Next))
  676.     if (debb->dn_Type == DLT_DEVICE && debb->dn_Task == s->lask) {
  677.         fart = gbip(debb->dn_Startup);
  678.         if (fart) {
  679.         if (!(feh = gbip(fart->fssm_Environ)))
  680.             break;
  681.         deb = gbip(fart->fssm_Device);
  682.         if (*deb > 59)
  683.             break;
  684.         strncpy(s->debna, (str) deb + 1, (size_t) *deb);
  685.         s->debna[*deb] = 0;
  686.         s->unit = fart->fssm_Unit;
  687.         s->flags = fart->fssm_Flags;
  688.         s->sex = feh[DE_BLKSPERTRACK];
  689.         s->sexize = feh[DE_SIZEBLOCK] << 2;
  690.         s->tish_offset = feh[DE_LOWCYL] * feh[DE_NUMHEADS] * s->sex;
  691.         if (feh[DE_TABLESIZE] >= DE_MAXTRANSFER) {
  692.             /* durn - this does NOT fix the Q40 guru bug ... */
  693.             mat = feh[DE_MAXTRANSFER] / s->sexize;
  694.             if (mat < s->sex) {
  695.             if (s->sex % mat)
  696.                 s->unit = -1;
  697.             else
  698.                 s->sex = mat;    /* pretend tracks are smaller */
  699. /**** cheap band-aid -- if you use maxtransfer it must be an even fraction */
  700. /*    of the track length or else I'll refuse to use it, for the present   */
  701.             }
  702.         }
  703.         if (feh[DE_TABLESIZE] >= DE_MASK)
  704.             s->memmask = feh[DE_MASK];
  705.         else s->memmask = 0xffffffffL;
  706.         if (feh[DE_TABLESIZE] >= DE_DOSTYPE) {
  707.             if (!(s->ffs = feh[DE_DOSTYPE] == 0x444f5301L))
  708.             if (feh[DE_DOSTYPE] != 0x444f5300L)
  709.                 s->unit = -1;
  710.         } else
  711.             s->ffs = false;
  712.         if (!s->sex || s->sexize != 512 || (s->ffs && !s->depth))
  713.             s->unit = -1;
  714.         /* ^v^v can't do non-512-byte blocks yet */
  715.         s->blize = (s->ffs ? 512 : 488);
  716.         s->tracklen = s->sex * s->sexize;
  717.         s->memtype = feh[DE_TABLESIZE] >= DE_MEMBUFTYPE ?
  718.                 feh[DE_MEMBUFTYPE] : MEMF_PUBLIC | MEMF_CHIP;
  719.         }
  720.         break;
  721.     }
  722.     Permit();
  723. }
  724.  
  725.  
  726.  
  727. private short OpenIt(SS)
  728. {
  729.     register short air;
  730.     s->ques.io_Message.mn_Node.ln_Type = NT_MESSAGE;
  731.     s->ques.io_Message.mn_Node.ln_Pri = 5;
  732.     {
  733.     register struct MsgPort **p = &s->ques.io_Message.mn_ReplyPort;
  734.     *p = CreatePort(null, 0L);
  735.     }
  736.     if (OpenDevice(s->debna, s->unit, (struct IORequest *) s->quest, s->flags)) {
  737.     DeletePort(s->ques.io_Message.mn_ReplyPort);
  738. #ifdef DEBUG_IO
  739.     putfmt("**** The OpenDevice failed, error %d!\n", s->ques.io_Error);
  740. #endif
  741.     return s->ques.io_Error;        /* no IoErr */
  742.     }
  743.     s->devopen = true;
  744.     s->ques.io_Command = TD_CHANGENUM;
  745.     DoIO((struct IORequest *) s->quest);
  746.     s->changes = s->ques.io_Actual;
  747.     air = s->ques.io_Error;
  748. #ifdef DEBUG_IO
  749.     if (air) putfmt("**** Hey!  OpenIt failed, hardware error %d!\n", air);
  750. #endif
  751.     s->readcommand = ETD_READ;        /* until we find it doesn't work */
  752.     return air;                /* no IoErr */
  753. }
  754.  
  755.  
  756.  
  757. private short GetTrack(long sect, SS)
  758. {
  759.     s->first_sector = (sect / s->sex) * s->sex;
  760.     s->mid_sector = s->first_sector + (sect % s->sex) >> 1;
  761.     do {
  762.     s->ques.io_Length = s->tracklen;
  763.     s->ques.io_Command = s->readcommand;
  764.     s->ques.io_Data = (adr) s->whole_track;
  765.     s->ques.io_Offset = s->sexize * (s->first_sector + s->tish_offset);
  766.     s->quest->iotd_Count = s->changes;
  767.     } while (DoIO((struct IORequest *) s->quest) && ManageError(s));
  768.     return s->ques.io_Error;
  769. }
  770.  
  771.  
  772.  
  773. private void GotASec(fiblet *foo, SS, fiblet *who)
  774. {
  775.     fiblet *k;
  776.     Push(&who->ready, foo);
  777.     if (foo->nexthash) {    /* nexthash is never already in wanted */
  778.     k = Extract(&s->maybe, foo->nexthash);
  779.     if (k) GotASec(k, s, who);
  780.     else WeWant(foo->nexthash, who->wanted);
  781.     }
  782. }
  783.  
  784.  
  785.  
  786. private bool ChewTrack(long lickey, SS, fiblet *who)
  787. {
  788.     short x;
  789.     register dosblock *jerk;
  790.     fiblet *foo, *bar;
  791.  
  792.     for (x = 0; x < s->sex; x++) {
  793.     jerk = s->whole_track + x;
  794.     if (jerk->Type == 2 && jerk->Type2 != 1 /* root dir */ &&
  795.                     (s->depth || jerk->Parent == lickey)
  796.                     && s->first_sector + x != lickey) {
  797.         bar = null;
  798.         if (foo = GetFiblet(s)) {
  799.         Chew(jerk, foo, s->blize);
  800.         if (YankIfPresent(foo->key, who->wanted))
  801.             GotASec(foo, s, who);
  802.         else
  803.             Insertt(&s->maybe, foo);
  804.         if (s->depth && foo->type > 0) {
  805.             if (!(bar = GetFiblet(s)))
  806.             return true;
  807.             if (DirChew(jerk, bar, s->blize)) {
  808.             s->me->pr_Result2 = ERROR_NO_FREE_STORE;
  809.             return true;
  810.             }
  811.             Push(&s->parents, bar);
  812.         }
  813.         } else
  814.         return true;
  815.     }
  816.     }
  817.     return false;
  818. }
  819.  
  820.  
  821.  
  822. private struct DeviceList *ValVol(struct FileLock *lick, struct Process *mee)
  823. {
  824.     register struct DeviceList *vawl = gbip(lick->fl_Volume);
  825.     if (vawl->dl_Type != DLT_VOLUME) {            /* not bulletproof */
  826.     mee->pr_Result2 = ERROR_INVALID_LOCK;
  827.     return null;
  828.     }                /* someday test for key in valid range? ****/
  829.     return vawl;
  830. }
  831.  
  832.  
  833.  
  834. private void Checkbies(register fiblet *who, SS)
  835. {
  836.     fiblet *foo;
  837.     short x;
  838.     for (x = 1; x <= *(who->wanted); x++)
  839.     if (who->wanted[x] && (foo = Extract(&s->maybe, who->wanted[x]))) {
  840.         who->wanted[x] = 0;
  841.         GotASec(foo, s, who);
  842.     }
  843. }
  844.  
  845.  
  846.  
  847. /* ================================ The four visible functions */
  848.  
  849. PUBLIC adr Get80(fib *f)
  850. {
  851.     register state *s = f->ss;
  852.     if ((long) s >= NEWSHALLOW || (long) s <= FALLTHRU)
  853.     return GetFiblet(s);
  854.     else return Alloc(80);
  855. }
  856.  
  857.  
  858.  
  859. PUBLIC void FastExCleanup(fib *f)
  860. {
  861.     register state *s = f->ss;
  862.     register fiblet *fent, *temp;
  863.  
  864.     f->ss = (adr) CLEANED;
  865.     if ((long) s >= FALLTHRU && (long) s <= NEWSHALLOW)
  866.     return;
  867.     if (s->devopen) {
  868.     s->ques.io_Length = 0;        /* motor off */
  869.     s->ques.io_Command = TD_MOTOR;
  870.     DoIO((struct IORequest *) s->quest);         /* io_Error?  fuck it. */
  871.     CloseDevice((struct IORequest *) s->quest);
  872.     DeletePort(s->ques.io_Message.mn_ReplyPort);
  873.     }
  874.     if (s->whole_track)
  875.     FreeMem(s->whole_track, s->tracklen);
  876.     if (s->quest)
  877.     Free(reek, s->quest);
  878.     FlushTree(&s->maybe);
  879. #ifdef FREEZIT
  880.     FlushList(&s->freeze);
  881. #endif
  882.     fent = s->parents;
  883.     while (fent) {
  884.     temp = fent->next;
  885.     FlushList(&fent->ready);
  886.     Freeblet(fent);
  887.     fent = temp;
  888.     }
  889.     Free(state, s);
  890. }
  891.  
  892.  
  893.  
  894. PUBLIC long FastExamine(BPTR lok, fib *fibb)
  895. {
  896.     struct FileLock *lick = gbip(lok);
  897.     struct Process *meee = ThisProcess();
  898.     long kee = lick->fl_Key;
  899.     bool nochew = false, deepe = (long) fibb->ss == NEWDEEP,
  900.             fall = (long) fibb->ss == FALLTHRU,
  901.             firsty = deepe | (long) fibb->ss == NEWSHALLOW;
  902.     fiblet *who = null;
  903.     struct DeviceList *vawl = ValVol(lick, meee);
  904.     register state *s;
  905.  
  906.     if (!lok || fall)
  907.     goto fallenback;
  908.     if (!vawl)
  909.     return DOSFALSE;
  910.     if (firsty) {
  911.     memset(fibb, 0, sizeof(fib));
  912.     if (!(fibb->ss = s = NewZ(state)))
  913.         goto fallenback;
  914.     s->me = meee;
  915.     s->depth = deepe;
  916.     s->lask = lick->fl_Task;
  917.     s->unit = -1;
  918.     s->vol = vawl;
  919.     s->me->pr_Result2 = 0;
  920.     s->mid_sector = 0;        /* one should start from the bottom */
  921.     if (s->quest || (s->quest = NewPZ(reek)))
  922.         FindDrive(s);
  923.     if (s->unit < 0 || !(who = GetFiblet(s))
  924.             || (who->snide = (adr) who->wanted = null, OpenIt(s))
  925.             || !(s->whole_track = AllocMem(s->tracklen, s->memtype))
  926.             || ((long) s->whole_track & ~s->memmask)   /* ???? ****/
  927.             || GetTrack(kee, s))    /** (AskTrack, WaitTrack) **/
  928.         goto fallback;
  929.     } else {
  930.     register fiblet **why;        /* is the block we're examining */
  931.     s = fibb->ss;            /* already in the parents list? */
  932.     why = &s->parents;
  933.     deepe = s->depth;
  934.     while (*why && (*why)->key != kee)
  935.         why = &(*why)->next;
  936.     if (who = *why) {
  937.         nochew = true;        /* yes, snip it out */
  938.         *why = who->next;
  939.     } else if (!(who = GetFiblet(s)) || GetTrack(kee, s))    /** Ask,Wait **/
  940.         goto fallback;
  941.     }
  942.     if (!nochew) {
  943.     if (DirChew(s->whole_track + kee % s->sex, who, s->blize))
  944.         goto fallback;
  945.     if (!who->key) who->key = kee;        /* special case: root dir */
  946.     }
  947.     ChewSomeMore(who, fibb);
  948.     Push(&s->parents, who);
  949.     fibb->pard = who;
  950.     s->me->pr_Result2 = 0;
  951.     if (who->type > 0) {
  952.     if (nochew)
  953.         Checkbies(who, s);
  954.     else if (ChewTrack(kee, s, who)) {
  955.         who = null;                /* prevent freeing twice */
  956.         goto fallback;
  957.     }
  958.     /** AskTrack(Nearest(s->mid_sector, who->wanted), s); **/
  959.     } else if (!deepe)
  960.     FastExCleanup(fibb);    /* not a directory */
  961.     s->lastwho = who;
  962.     return DOSTRUE;
  963. fallback:
  964. #ifdef DEBUG_IO
  965.     if (s->unit >= 0 && !s->whole_track)
  966.     put("**** Unable to allocate track buffer!  (Or something)\n");
  967. #endif
  968.     if (!deepe || firsty)
  969.     FastExCleanup(fibb);
  970.     if (who) Freeblet(who);
  971. fallenback:
  972.     if (meee->pr_Result2 == ERROR_DEVICE_NOT_MOUNTED
  973.         /* || meee->pr_Result2 == ERROR_NOT_A_DOS_DISK */ )
  974.     return DOSFALSE;
  975.     fibb->ss = (adr) FALLTHRU;
  976.     return Examine(lok, (struct FileInfoBlock *) fibb);
  977. }
  978.  
  979.  
  980.  
  981. PUBLIC long FastExNext(BPTR lok, fib *fibb)
  982. {
  983.     struct FileLock *lick = gbip(lok);
  984.     fiblet *gut;
  985.     long loew, kee = lick->fl_Key;
  986.     register state *s = fibb->ss;
  987.     register fiblet *who = fibb->pard;
  988.     struct Process *meee = ThisProcess();
  989.  
  990.     if ((long) s == CLEANED) {
  991.     meee->pr_Result2 = ERROR_NO_MORE_ENTRIES;
  992.     return DOSFALSE;
  993.     }
  994.     if (!lok || (long) s == FALLTHRU)
  995.     return ExNext(lok, (struct FileInfoBlock *) fibb);
  996.     if (!ValVol(lick, meee))
  997.     return DOSFALSE;
  998.     if (who != s->lastwho)
  999.     Checkbies(who, s);
  1000.     while (!(gut = Pop(&who->ready))) {
  1001.     /** if (!s->sectpending) AskTrack(s->wanna_ask); **/
  1002.     /* sectpending is set by AskTrack and cleared by WaitTrack */
  1003.     /** if (WaitTrack(s) || ChewTrack(kee, s, who)) goto bomb; **/
  1004.     if (!(loew = Nearest(s->mid_sector, who->wanted))) {
  1005.         s->me->pr_Result2 = ERROR_NO_MORE_ENTRIES;
  1006.         goto bomb;
  1007.     }
  1008.     if (GetTrack(loew, s) || ChewTrack(kee, s, who))    /** remove **/
  1009.         goto bomb;
  1010.     /** if (who->ready && who->ready->type < 0) AskTrack(loew, s);
  1011.         else s->wanna_ask = loew; **/
  1012.     /**** The following is a somewhat inefficient defense against an
  1013.         unlikely condition: file expected in table not present. */
  1014.     if (!who->ready)
  1015.         YankIfPresent(loew, who->wanted);        /* don't try again */
  1016.     }
  1017.     ChewSomeMore(gut, fibb);
  1018. #ifdef FREEZIT
  1019.     {
  1020.                     register fiblet *fuu = (adr) gut->snide;
  1021.     if (fuu) {
  1022.         fuu->wanted = (adr) fuu->snide = gut->snide = null;
  1023.         Push(&s->freeze, fuu);          /* comment and fiblet same size */
  1024.     }
  1025.     }
  1026.     if (gut->wanted) {
  1027.     FreeMem(gut->wanted, tablesize << 2);
  1028.     gut->wanted = null;
  1029.     }
  1030.     Push(&s->freeze, gut);
  1031. #else
  1032.     Freeblet(gut);
  1033. #endif
  1034.     s->me->pr_Result2 = 0;
  1035.     s->lastwho = who;
  1036.     return DOSTRUE;
  1037. bomb:
  1038.     if (!s->depth)
  1039.     FastExCleanup(fibb);
  1040.     return DOSFALSE;
  1041. }
  1042.